// Test for the model::url interface
// Copyright 2004 (C) Ralph Thomas

#include <model/internal/url.h>
#include <tests/testCore.h>
#include <util/log.h>

#include <iostream>

namespace model {
	//
	/// This test exercises the parsing ability of the model::url interface.
	/// It tries both good and bad inputs in an effort to make the parser
	/// slip up.
	//
	class testUrlParser : public test::basic {
	  public:
		const char* performTest() {
			util::string urlString = "test://localtest/file";
			url* myUrl = url::fromString( urlString );

			if( !myUrl ) return "Parsing failed for valid input";
			if( myUrl->getCompleteUrl() != urlString ) return "myUrl->getCompleteUrl() did not return expected URL";
			if( myUrl->getPrintableUrl() != urlString ) return "myUrl->getPrintableUrl() did not return expected URL";
			if( myUrl->getProtocol() != "test" ) return "myUrl->getProtocol() did not return \"test\"";
			if( myUrl->getHostname() != "localtest" ) return "myUrl->getHostname() did not return \"localtest\"";
			if( myUrl->getPath() != "file" ) return "myUrl->getPath() did not return \"file\"";
			if( myUrl->getPresentParts() != (url::kProtocol | url::kHostname | url::kPath) ) return "myUrl->getPresentParts() did not return expected list (protocol, hostname, path)";
			myUrl->setPath( "" );
			if( myUrl->getPresentParts() != (url::kProtocol | url::kHostname) ) return "myUrl->setPath( \"\" ) did not stop getPresentParts() from reporting a path part";
			myUrl->setPath( "foo/bar/baz" );
			if( myUrl->getCompleteUrl() != "test://localtest/foo/bar/baz" ) return "myUrl->setPath() did not update complete path correctly";


			delete myUrl;

			//
			// Test two, a more elaborate URL.
			//
			urlString = "test://user123:pass234@hostname.com:2323/fairly/long/path";
			myUrl = url::fromString( urlString );

			if( !myUrl ) return "couldn't parse second URL";
			if( myUrl->getCompleteUrl() != urlString ) {
				std::cout << "complete url: " << myUrl->getCompleteUrl() << std::endl;
				std::cout << "original url: " << urlString << std::endl;
				return "myUrl returned unexpected complete URL #2";
			}
			if( myUrl->getPrintableUrl() != "test://user123@hostname.com:2323/fairly/long/path" ) return "myUrl didn't remove password properly #2";
			if( myUrl->getProtocol() != "test" ) return "myUrl->getProtocol() did not return \"test\" #2";
			if( myUrl->getUsername() != "user123" ) return "myUrl->getUsername() did not return \"user123\" #2";
			if( myUrl->getPassword() != "pass234" ) return "myUrl->getPassword() did not return \"pass234\" #2";
			if( myUrl->getHostname() != "hostname.com" ) return "myUrl->getHostname() did not return \"hostname.com\"";
			if( myUrl->getPort() != 2323 ) return "myUrl->getPort() did not return 2323";
			if( myUrl->getPath() != "fairly/long/path" ) return "myUrl->getPath() did not return \"/fairly/long/path\" #2";
			if( myUrl->getPresentParts() != (url::kProtocol|url::kUsername|url::kPassword|url::kHostname|url::kPort|url::kPath) ) return "myUrl->getPresentParts() did not list all parts present #2";
			
			delete myUrl;

			//
			// Test three, an IP address and only a username.
			//
			urlString = "testME://ralpht@144.253.68.88:22/test/Zees";
			myUrl = url::fromString( urlString );

			if( !myUrl ) return "couldn't parse URL #3";
			if( myUrl->getProtocol() != "testME" ) return "myUrl->getProtocol() did not return \"testME\" #3";
			if( myUrl->getUsername() != "ralpht" ) return "myUrl->getUsername() did not return \"ralpht\" #3";
			if( myUrl->getHostname() != "144.253.68.88" ) return "myUrl->getHostname() did not return \"144.253.68.88\" #3";
			if( myUrl->getPort() != 22 ) return "myUrl->getPort() did not return 22 #3";
			if( myUrl->getPath() != "test/Zees" ) return "myUrl->getPath() did not return \"/test/Zees\" #3";
			if( myUrl->getPresentParts() != (url::kProtocol|url::kUsername|url::kHostname|url::kPort|url::kPath) ) return "myUrl->getPresentParts() did not return expected list of parts (protocl, username, hostname, port, path)";
			if( myUrl->getPassword() != "" ) return "myUrl->getPassword() has mysterious non empty value";
			delete myUrl;
			//
			// Test four, a path with spaces!
			//
			urlString = "testMe://foo@bar.com/Oh My Goodness/There Are Spaces /In My / URL?what=really&oh=no";
			myUrl = url::fromString( urlString );

			if( !myUrl ) return "couldn't parse URL #4";
			if( myUrl->getProtocol() != "testMe" ) return "myUrl->getProtocol() did not return \"testMe\" #4";
			if( myUrl->getUsername() != "foo" ) return "myUrl->getUsername() did not return \"foo\" #4";
			if( myUrl->getHostname() != "bar.com" ) return "myUrl->getHostname() did not return \"bar.com\" #4";
			if( myUrl->getPath() != "Oh My Goodness/There Are Spaces /In My / URL" ) return "myUrl->getPath() returned bad path #4";
			if( myUrl->getPresentParts() != (url::kProtocol|url::kUsername|url::kHostname|url::kPath|url::kQuery) ) return "myUrl->getPresentParts() returned unexpected part list #4";
			if( myUrl->getPassword() != "" ) return "myUrl->getPassword has non-empty value #4";
			//
			// Now try a few which won't parse.
			//
			urlString = "mailto:ralpht@gmail.com";
			myUrl = url::fromString( urlString );
			if( myUrl ) return "mailto: URI parsed ok!";

			urlString = "test://user@@host:zys/boing";
			myUrl = url::fromString( urlString );
			if( myUrl ) return "junk URL parsed ok!";

			return NULL;
		}

		const char* getTestName() {
			return "Exercise parsing ability of model::url";
		}
	};
	//
	/// The testUrlEquals test exercises the equals method of the URL class.
	//
	class testUrlEquals : public test::basic {
	  public:
		const char* performTest() {
			url* a = url::fromString( "http://ralpht@localhost:8080/file?arg=foo&arg2=bar" );
			if( !a ) return "couldn't create initial URL";
			url* ac = url::fromUrl( a );
			if( !ac ) return "couldn't create copied URL";
			if( !a->equals( ac ) ) return "URL doesn't equal it's copy";
			if( !ac->equals( a ) ) return "copied URL doesn't equal original";
			url* b = url::fromString( "ftp://nobody@localhost:8020/pub/bar/baz" );
			if( a->equals( b ) ) return "URL a equals URL b";
			if( b->equals( a ) ) return "URL b equals URL a";
			delete a;
			delete b;
			delete ac;
			return NULL;
		}

		const char* getTestName() {
			return "Exercise equals method of model::url";
		}
	};
	//
	/// The testUrlArgs test exercises the argument parsing of the URL class.
	//
	class testUrlArgs : public test::basic {
	  public:
		const char* performTest() {
			url* noargs = url::fromString( "http://www.infinite-imagination.com/script" );
			url* args = url::fromString( "http://www.infinite-imagination.com/script?foo=bar&bar=baz&zz" );

			if( !noargs ) return "couldn't create no argument URL";
			if( noargs->getPresentParts() & url::kQuery ) return "no argument URL reporting having a query section";
			if( noargs->getNumberArguments() != 0 ) return "no argument URL reports having more than zero arguments";

			if( !args ) return "couldn't create URL with arguments";
			if( (args->getPresentParts() & url::kQuery) == 0 ) return "URL with arguments reports no query section";
			if( args->getNumberArguments() != 3 ) return "URL with 3 arguments reports some other number";
			if( args->equals( noargs ) ) return "argument and no argument URLs are apparently equal";
			if( noargs->equals( args ) ) return "no argument and argument URLs are apparently equal";
			delete noargs;
			if( args->getArgumentValue( "foo" ) != "bar" ) return "argument value for \"foo\" was not \"bar\"";
			if( args->getArgumentValue( "bar" ) != "baz" ) return "argument value for \"bar\" was not \"baz\"";
			if( args->getArgumentValue( "zz" ) != "" ) return "argument value for \"zz\" was not \"\"";
			if( args->getArgumentValue( "made-up" ) != "" ) return "made up argument key had value";

			bool gotFoo = false; bool gotBar = false; bool gotZz = false;
			for( unsigned int i = 0; i < 3; i++ ) {
				if( args->getArgumentKey( i ) == "foo" ) {
					if( gotFoo ) return "found \"foo\" key twice";
					gotFoo = true;
					if( args->getArgumentValue( i ) != "bar" ) return "accessing pair foo/bar by index gave wrong value";
				} else if( args->getArgumentKey( i ) == "bar" ) {
					if( gotBar ) return "found \"bar\" key twice";
					gotBar = true;
					if( args->getArgumentValue( i ) != "baz" ) return "accessing pair bar/baz by index gave wrong value";
				} else if( args->getArgumentKey( i ) == "zz" ) {
					if( gotZz ) return "found \"zz\" key twice";
					gotZz = true;
					if( args->getArgumentValue( i ) != "" ) return "accessing pair zz/ by index gave wrong value";
				} else return "found unknown key";
			}
			if( !gotFoo ) return "didn't find \"foo\" key";
			if( !gotBar ) return "didn't find \"bar\" key";
			if( !gotZz ) return "didn't find \"zz\" key";

			if( args->getCompleteUrl() != "http://www.infinite-imagination.com/script?foo=bar&bar=baz&zz" ) return "complete URL is invalid";
			if( args->getPrintableUrl() != args->getCompleteUrl() ) return "printable URL is invalid";

			delete args;
			return NULL;
		}

		const char* getTestName() {
			return "Exercise argument parsing of model::url";
		}
	};
	//
	/// Test the URLs ability to return strings of itself.
	//
	class testUrlGet : public test::basic {
	  public:
		const char* performTest() { 
			util::string urlString = "testFoo://ralpht:secret@bar.com/foo/head?bar=baz";
			url* myUrl = url::fromString( urlString );

			if( !myUrl ) return "couldn't create URL to test with!";
			if( myUrl->getCompleteUrl() != urlString ) return "incorrect complete URL";
			if( myUrl->getPrintableUrl() != "testFoo://ralpht@bar.com/foo/head?bar=baz" ) return "incorrect printable URL";
			if( myUrl->getUrlWithoutParts( 0 ) != myUrl->getCompleteUrl() ) return "URL without \"no\" parts isn't complete URL";
			if( myUrl->getUrlWithoutParts( url::kPath ) != "testFoo://ralpht:secret@bar.com/?bar=baz" ) return "bad URL without path";
			if( myUrl->getUrlWithoutParts( url::kQuery ) != "testFoo://ralpht:secret@bar.com/foo/head" ) return "bad URL without query";
			if( myUrl->getUrlWithoutParts( url::kPath | url::kQuery ) != "testFoo://ralpht:secret@bar.com/" ) return "bad URL without path and query";
			myUrl->setPath( "oh/no/you/dont" );
			if( myUrl->getCompleteUrl() != "testFoo://ralpht:secret@bar.com/oh/no/you/dont?bar=baz" ) return "complete URL didn't update";
			if( myUrl->getPrintableUrl() != "testFoo://ralpht@bar.com/oh/no/you/dont?bar=baz" ) return "printable URL didn't update";
			delete myUrl;
			return NULL;
		}
		const char* getTestName() { return "Exercise get... methods of model::url"; }
	};
};

void test::runnerSingleton::registerTests() {
	adoptTest( new model::testUrlParser() );
	adoptTest( new model::testUrlEquals() );
	adoptTest( new model::testUrlArgs() );
	adoptTest( new model::testUrlGet() );
}

